package com.sky.service.impl.admin;

import com.sky.Do.ReportDo;
import com.sky.dto.GoodsSalesDTO;
import com.sky.entity.Orders;
import com.sky.mapper.admin.ReportMapper;
import com.sky.result.Result;
import com.sky.service.admin.ReportService;
import com.sky.service.admin.WorkSpaceService;
import com.sky.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.*;


@Service
@Slf4j
public class ReportServiceImpl implements ReportService {

    @Autowired
    private ReportMapper reportMapper;

    @Autowired
    private WorkSpaceService workSpaceService;

    @Override
    public Result<TurnoverReportVO> turnoverStatisticsByTime(LocalDate begin, LocalDate end) {
        // 根据传递的开始和结束时间获取到时间段内的当天日期
        // 根据传递的开始和结束时间获取到时间段内的当天日期
        List<LocalDate> dateList = getDateList(begin, end);


        // 获取到时间段内每一天的营业额
        List<Double> businessList = new ArrayList<>();

        dateList.forEach(e -> {
            // 大于当天最初时间 小于当天最晚时间
            LocalDateTime beginDateTime = LocalDateTime.of(e, LocalTime.MIN);
            LocalDateTime endDateTime = LocalDateTime.of(e, LocalTime.MAX);

            // 封装判断条件
            Map<String, Object> map = new HashMap<>();
            map.put("status", Orders.COMPLETED);
            map.put("beginTime", beginDateTime);
            map.put("endTime", endDateTime);

            // 对返回的null值进行处理
            Double business = reportMapper.getOrderBusinessByMap(map) == null ? 0 : reportMapper.getOrderBusinessByMap(map);
            businessList.add(business);
        });

        // 封装返回结果集
        TurnoverReportVO vo = TurnoverReportVO
                .builder()
                .dateList(StringUtils.join(dateList, ","))
                .turnoverList(StringUtils.join(businessList, ","))
                .build();

        return Result.success(vo);
    }

    @Override
    public Result<UserReportVO> userStatisticsByTime(LocalDate begin, LocalDate end) {
        // 根据传递的开始和结束时间获取到时间段内的当天日期
        List<LocalDate> dateList = getDateList(begin, end);

        //获取当天总用户量列表
        List<Long> totalUserCountList = new ArrayList<>();

        //获取当天新用户量列表
        List<Long> newUserCountList = new ArrayList<>();

        // todo ： 注释代码曲解了需求, 统计的用户量为当天下单成功的全部用户量和新用户量
////        dateList.forEach(e -> {
//            // 大于当天最初时间 小于当天最晚时间
//            LocalDateTime beginTime = LocalDateTime.of(e, LocalTime.MIN);
//            LocalDateTime endTime = LocalDateTime.of(e, LocalTime.MAX);
//
//            // 封装判断条件
//            Map<String, Object> map = new HashMap<>();
//            map.put("status", Orders.COMPLETED);
//            map.put("beginTime", beginTime);
//            map.put("endTime", endTime);
//
//            // 获取到当天下单成功用户的id
//            List<Long> ids = reportMapper.getUserIdOfCompltedOrderOnCurrenDateByMap(map);
//            // 将查询不到返回的null值映射成0后返回id集合长度
//            totalUserCountList.add(ids.stream().map(id -> {
//                if (id == null) return 0;
//                return id;
//            }).count());
//
//           List<Long> newUserIdlist = reportMapper.getNewUserIdOfCompltedOrderOnCurrenDateByMap(map);
//            // 将查询不到返回的null值映射成0后返回id集合长度
//            newUserCountList.add(newUserIdlist.stream().map(id -> {
//                if (id == null) return 0;
//                return id;
//            }).count());
//
//        });

        // todo : 实际需求：统计的全部用户量和新用户量 应为登录进入小程序的用户量 不需要判断是否下单
        dateList.forEach(e -> {
            // 大于当天最初时间 小于当天最晚时间
            LocalDateTime beginTime = LocalDateTime.of(e, LocalTime.MIN);
            LocalDateTime endTime = LocalDateTime.of(e, LocalTime.MAX);
            totalUserCountList.add(reportMapper.getUserCountByTime(null, endTime));
            newUserCountList.add(reportMapper.getUserCountByTime(beginTime, endTime));
        });

        UserReportVO vo = UserReportVO
                .builder()
                .newUserList(StringUtils.join(newUserCountList, ","))
                .totalUserList(StringUtils.join(totalUserCountList, ","))
                .dateList(StringUtils.join(dateList, ","))
                .build();

        return Result.success(vo);
    }

    @Override
    public Result<OrderReportVO> ordersStatisticsByTime(LocalDate begin, LocalDate end) {
        // 获取时间段内的每一天日期
        List<LocalDate> dateList = getDateList(begin, end);

        // 每日订单数量集合
        List<Integer> everyDayOrderCountList = new ArrayList<>();

        // 每日有效订单数量集合
        List<Integer> everyDayCompletedOrderList = new ArrayList<>();

        dateList.forEach(e -> {
            LocalDateTime beginDateTime = LocalDateTime.of(e, LocalTime.MIN);
            LocalDateTime endDateTime = LocalDateTime.of(e, LocalTime.MAX);

            // 订单查询条件结果集对象
            ReportDo reportDo = ReportDo
                    .builder()
                    .begin(beginDateTime)
                    .end(endDateTime)
                    .build();

            // 获取每日订单数
            everyDayOrderCountList.add(reportMapper.getOrderCountByReprotDo(reportDo));

            // 获取每日有效订单数
            reportDo.setStatus(Orders.COMPLETED);
            everyDayCompletedOrderList.add(reportMapper.getOrderCountByReprotDo(reportDo));
        });

        // 获取订单总数
        ReportDo reportDo = ReportDo
                .builder()
                .end(LocalDateTime.of(dateList.get(dateList.size() - 1), LocalTime.MAX))
                .build();
        Integer totalOrderCount = reportMapper.getOrderCountByReprotDo(reportDo);

        // 获取有效订单数
        reportDo.setStatus(Orders.COMPLETED);
        Integer validOrderCount = reportMapper.getOrderCountByReprotDo(reportDo);

        // 订单完成率 = 有效订单 / 订单总数
        Double orderCompleteionRate =
                (totalOrderCount.equals(0) || validOrderCount.equals(0))
                        ? 0
                        : Double.valueOf(validOrderCount) / Double.valueOf(totalOrderCount);

        OrderReportVO vo = OrderReportVO
                .builder()
                .dateList(StringUtils.join(dateList, ","))
                .orderCountList(StringUtils.join(everyDayOrderCountList, ","))
                .validOrderCountList(StringUtils.join(everyDayCompletedOrderList, ","))
                .totalOrderCount(totalOrderCount)
                .validOrderCount(validOrderCount)
                .orderCompletionRate(orderCompleteionRate)
                .build();

        return Result.success(vo);
    }

    // 查询指定时间区间内的销量排名top10
    @Override
    public Result<SalesTop10ReportVO> salesTop10Report(LocalDate begin, LocalDate end) {
        // 根据时间区间内的商品销量top10信息
        LocalDateTime beginDateTime = LocalDateTime.of(begin, LocalTime.MIN);
        LocalDateTime endDateTime = LocalDateTime.of(end, LocalTime.MAX);

        List<GoodsSalesDTO> top10GoodsInfoList = reportMapper.getTop10GoodsInfoListByTimeLine(beginDateTime, endDateTime);

        //封装结果集
        SalesTop10ReportVO vo = SalesTop10ReportVO
                .builder()
                .nameList(StringUtils.join(top10GoodsInfoList.stream().map(GoodsSalesDTO::getName).toList(), ","))
                .numberList(StringUtils.join(top10GoodsInfoList.stream().map(GoodsSalesDTO::getNumber).toList(), ","))
                .build();

        return Result.success(vo);
    }

    // 导出近30天的营收数据
    @Override
    public void exportExcel(HttpServletResponse response) {
        // 获取到近30天内的营收数据
        // 如：今天为 2023/3/29  获取到的区间为:  2023/2/29 - 2023/3/28
        LocalDateTime begin = LocalDateTime.of(LocalDate.now(), LocalTime.MIN).minusDays(30);
        LocalDateTime end = LocalDateTime.of(LocalDate.now(), LocalTime.MIN).minusDays(1);
        BusinessDataVO vo = formatBusinessDate(workSpaceService.businessData(begin, end).getData());
        // 将近30天内的营收数据写入模版中
        BufferedInputStream bi = null;
        XSSFWorkbook excel = null;
        try {
            // 读取模版创建excel对象
            bi = new BufferedInputStream(this.getClass().getClassLoader().getResourceAsStream("template/运营数据报表模板.xlsx"));
            excel = new XSSFWorkbook(bi);
            // 获取到模版首页
            XSSFSheet sheet = excel.getSheetAt(0);
            // 设置第二行的内容为 时间区间 如 ：2023/2/29 - 2023/3/28
            sheet.getRow(1).getCell(1).setCellValue(begin.toLocalDate() + " - " + end.toLocalDate());

            // 写入概览数据部分

            // 写入 营业额,订单完成率，新增用户量数据
            XSSFRow row = sheet.getRow(3);
            row.getCell(2).setCellValue(vo.getTurnover());
            row.getCell(4).setCellValue(vo.getOrderCompletionRate());
            row.getCell(6).setCellValue(vo.getNewUsers());

            // 写入 有效订单，平均客单价数据
            row = sheet.getRow(4);
            row.getCell(2).setCellValue(vo.getValidOrderCount());
            row.getCell(4).setCellValue(vo.getUnitPrice());

            // 写入明细数据部分
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
            for (int i = 0; i < 30; i++) {
                LocalDateTime beginDateTime = begin.plusDays(i);
                LocalDateTime endDateTime = LocalDateTime.of(beginDateTime.toLocalDate(),LocalTime.MAX);
                vo = formatBusinessDate(workSpaceService.businessData(beginDateTime,endDateTime).getData());

                row = sheet.getRow(7 + i);
                row.getCell(1).setCellValue(dateTimeFormatter.format(beginDateTime.toLocalDate()));
                row.getCell(2).setCellValue(vo.getTurnover());
                row.getCell(3).setCellValue(vo.getValidOrderCount());
                row.getCell(4).setCellValue(vo.getOrderCompletionRate());
                row.getCell(5).setCellValue(vo.getUnitPrice());
                row.getCell(6).setCellValue(vo.getNewUsers());
            }

            // 将录入完数据的模版上传到客户端浏览器
            ServletOutputStream outputStream = response.getOutputStream();
            excel.write(outputStream);
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (bi != null) bi.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

            try {
                if (excel != null) excel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }

    }

    // 该方法用于将实体类中的在mysql中查询不到数据的null值转化为0
    private BusinessDataVO formatBusinessDate(BusinessDataVO vo){
        if (vo == null) throw new NullPointerException("格式化的对象为空");
        if (vo.getTurnover() == null) vo.setTurnover(0.00);
        if (vo.getValidOrderCount() == null) vo.setValidOrderCount(0);
        if (vo.getOrderCompletionRate() == null) vo.setOrderCompletionRate(0.00);
        if (vo.getUnitPrice() == null) vo.setUnitPrice(0.00);
        if (vo.getNewUsers() == null) vo.setNewUsers(0);

        return vo;
    }


    // 该方法用于获取时间头节点和尾节点中的每一天的日期
    // 返回值：时间日期以  List<LocalDate> 的形式返回
    // 形参： begin 时间头节点  end 时间尾节点
    private List<LocalDate> getDateList(LocalDate begin, LocalDate end) {
        if (begin == null || end == null) return Collections.emptyList();
        List<LocalDate> dateList = new ArrayList<>();
        dateList.add(begin);

        while (!begin.equals(end)) {
            begin = begin.plusDays(1);
            dateList.add(begin);
        }
        return dateList;
    }

}
